package yawning.mew.cat;

import java.util.HashMap;
import java.util.Map;

import yawning.mew.ProcEffect.Trigger;
import yawning.mew.cat.CatModelSimState.CatTargetData;
import yawning.mew.character.CharacterModel.AttackResult;
import yawning.mew.sim.SimulationAction;
import yawning.mew.sim.SimulationTarget;

public class SwipeAction implements SimulationAction {
	CatModelSimState mState;
	Map<SimulationTarget,SwipeStats> mDamageStats;

	class SwipeStats {
		long mSwipeDamage;
		int mNrSwipeMiss;
		int mNrSwipeDodge;
		int mNrSwipeParry;
		int mNrSwipeCrit;
		int mNrSwipeHit;
	}

	public SwipeAction(CatModelSimState state) {
		mState = state;
		mDamageStats = new HashMap<SimulationTarget,SwipeStats>();

		// Pre-allocate stats for all known targets (Yawningstraza).
		for (SimulationTarget target: mState.mTargetMap.values()) {
			mDamageStats.put(target, new SwipeStats());
		}
	}

	@Override
	public double perform() {
		int cost = mState.getEnergyCost(mState.mModel.swipeCost);
		int prevEnergy = mState.getEnergy();

		if (mState.getEnergy() < cost && !mState.mOocEv.isUp()) return SimulationAction.ACTION_FAIL;

		int nrTargets = mState.getTargetCount();

		// BUG: Does not benefit from Bonus Weapon Damage (4.0.6 PTR)
		double damage = mState.mModel.getWeaponDamage(true) * mState.mModel.multSwipe;
		double aoeDamageCap = damage * 10;	// Damage is pre-crit, capped @ 10 targets.
		damage = (nrTargets > 10) ? aoeDamageCap / nrTargets : damage;

		SimulationTarget currentTarget = mState.mActiveTarget;

		for (String targetName : mState.getTargetList()) {
			SimulationTarget target = mState.mTargetMap.get(targetName);
			CatTargetData targetData = (CatTargetData) target.getTargetData();
			// Skip dead targets.
			if (!targetData.mDeathEv.isUp()) {
				continue;
			}

			// TODO: I should provide the option for modeling the fact that some mobs may be facing me.
			mState.mModel.setTarget(target.getTarget());	// Use the target's combat table.
			AttackResult result = (targetData.mCanParry) ? mState.mModel.getFrontalYellowResult() : mState.mModel.getYellowResult();

			SwipeStats stats = mDamageStats.get(target);
			if (stats == null) {
				stats = new SwipeStats();
				mDamageStats.put(target, stats);
			}

			if (result == AttackResult.MISS) {
				stats.mNrSwipeMiss++;

				if (mState.mIsLogging) mState.mLog.log("Swipe: %s MISS\n", targetName);
			} else if (result == AttackResult.DODGE) {
				stats.mNrSwipeDodge++;

				if (mState.mIsLogging) mState.mLog.log("Swipe: %s DODGE\n", targetName);
			} else if (result == AttackResult.PARRY) {
				stats.mNrSwipeParry++;

				if (mState.mIsLogging) mState.mLog.log("Swipe: %s PARRY\n", targetName);
			} else {
				double targetDamage = damage;

				if (result == AttackResult.CRIT) {
					stats.mNrSwipeCrit++;
					targetDamage *= mState.mModel.multCrit;
				} else {
					stats.mNrSwipeHit++;
				}

				targetDamage *= mState.getDirectDamageMult(target);
				stats.mSwipeDamage += Math.round(targetDamage);

				if (mState.mIsLogging) mState.mLog.log("Swipe: %s %s Damage: %d\n", targetName, result, Math.round(damage));

				if (result == AttackResult.CRIT) mState.mSimEngine.triggerProc(target, Trigger.YELLOWCRIT); else mState.mSimEngine.triggerProc(target, Trigger.YELLOW);
			}
		}

		mState.mModel.setTarget(currentTarget.getTarget());

		if (mState.mOocEv.isUp()) mNrSwipeOoC++;
		if (mState.mTigersFuryEv.isUp()) mNrSwipeTF++;
		mNrSwipes++;

		mSwipeEnergy += mState.onOoCConsumer(cost);

		mState.logResourceChange(prevEnergy, mState.getComboPoints());

		return 1.0f;
	}

	int mNrSwipes;

	int mSwipeEnergy;
	int mNrSwipeOoC;
	int mNrSwipeTF;
}
